use anyhow::Result;
use std::process::{Command, Stdio};

cfg_if::cfg_if! {
    if #[cfg(windows)] {
        use windows::Win32::System::Power::{
            SetThreadExecutionState, EXECUTION_STATE, ES_CONTINUOUS,
            ES_SYSTEM_REQUIRED, ES_DISPLAY_REQUIRED
        };
        use std::sync::atomic::{AtomicBool, Ordering};
    }
}

pub struct SleepInhibitor {
    #[cfg(unix)]
    child: Option<std::process::Child>,
    #[cfg(windows)]
    is_active: std::sync::Arc<AtomicBool>,
}

impl SleepInhibitor {
    pub async fn new() -> Result<Self> {
        cfg_if::cfg_if! {
            if #[cfg(unix)] {
                Self::new_unix().await
            } else if #[cfg(windows)] {
                Self::new_windows().await
            } else {
                anyhow::bail!("Sleep inhibition not supported on this platform");
            }
        }
    }

    #[cfg(unix)]
    async fn new_unix() -> Result<Self> {
        // Try different methods based on the platform
        if cfg!(target_os = "macos") {
            // Use caffeinate on macOS
            let mut child = Command::new("caffeinate")
                .arg("-d") // Prevent system sleep
                .arg("-i") // Prevent idle sleep
                .arg("-u") // Prevent the system from sleeping due to inactivity
                .stdin(Stdio::null())
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .spawn()
                .map_err(|e| anyhow::anyhow!("Failed to spawn caffeinate: {}", e))?;

            // Give it a moment to start
            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

            // Check if the process is still running
            match child.try_wait()? {
                Some(status) => {
                    anyhow::bail!("caffeinate process exited with status: {}", status);
                }
                None => {
                    // Process is still running, which is what we want
                    Ok(Self { child: Some(child) })
                }
            }
        } else if cfg!(target_os = "linux") {
            // Try systemd-inhibit first on Linux
            if let Ok(inhibitor) = Self::try_systemd_inhibit() {
                return Ok(inhibitor);
            }

            // Fallback to xdg-screensaver
            #[cfg(target_os = "linux")]
            {
                Self::try_xdg_screensaver().await
            }
            #[cfg(not(target_os = "linux"))]
            {
                anyhow::bail!("Sleep inhibition not supported on this platform");
            }
        } else {
            // Generic Unix - try to use a simple approach
            anyhow::bail!("Sleep inhibition not supported on this Unix platform");
        }
    }

    #[cfg(unix)]
    fn try_systemd_inhibit() -> Result<Self> {
        let child = Command::new("systemd-inhibit")
            .arg("--what=sleep")
            .arg("--who=iroh-chat-term")
            .arg("--why=Terminal chat session is active")
            .arg("--mode=block")
            .arg("sleep")
            .arg("infinity")
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .map_err(|e| anyhow::anyhow!("Failed to spawn systemd-inhibit: {}", e))?;

        Ok(Self { child: Some(child) })
    }

    #[cfg(unix)]
    #[cfg(target_os = "linux")]
    async fn try_xdg_screensaver() -> Result<Self> {
        // Create a simple process that periodically calls xdg-screensaver reset
        let mut child = Command::new("sh")
            .arg("-c")
            .arg("while true; do xdg-screensaver reset; sleep 30; done")
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .map_err(|e| anyhow::anyhow!("Failed to spawn xdg-screensaver loop: {}", e))?;

        // Give it a moment to start
        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

        // Check if the process is still running
        match child.try_wait()? {
            Some(status) => {
                anyhow::bail!("xdg-screensaver process exited with status: {}", status);
            }
            None => {
                // Process is still running, which is what we want
                Ok(Self { child: Some(child) })
            }
        }
    }

    #[cfg(windows)]
    async fn new_windows() -> Result<Self> {
        let is_active = std::sync::Arc::new(AtomicBool::new(false));

        unsafe {
            let flags = ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_CONTINUOUS;
            let result = SetThreadExecutionState(EXECUTION_STATE(flags));

            if result.is_empty() {
                anyhow::bail!("Failed to set execution state");
            }
        }

        is_active.store(true, Ordering::SeqCst);

        // Start a background task to keep the system awake
        let is_active_clone = is_active.clone();
        tokio::spawn(async move {
            let mut interval = tokio::time::interval(std::time::Duration::from_secs(30));

            while is_active_clone.load(Ordering::SeqCst) {
                interval.tick().await;

                unsafe {
                    let flags = ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_CONTINUOUS;
                    let result = SetThreadExecutionState(EXECUTION_STATE(flags));

                    if result.is_empty() {
                        eprintln!("Warning: Failed to maintain sleep inhibition");
                        break;
                    }
                }
            }
        });

        Ok(Self { is_active })
    }

    pub async fn release(mut self) -> Result<()> {
        cfg_if::cfg_if! {
            if #[cfg(unix)] {
                if let Some(mut child) = self.child.take() {
                    child.kill().map_err(|e| anyhow::anyhow!("Failed to kill sleep inhibitor process: {}", e))?;
                    child.wait().map_err(|e| anyhow::anyhow!("Failed to wait for sleep inhibitor process: {}", e))?;
                }
                Ok(())
            } else if #[cfg(windows)] {
                self.release_windows().await
            } else {
                Ok(())
            }
        }
    }

    #[cfg(windows)]
    async fn release_windows(self) -> Result<()> {
        self.is_active.store(false, Ordering::SeqCst);

        unsafe {
            let result = SetThreadExecutionState(EXECUTION_STATE::ES_CONTINUOUS);

            if result.is_empty() {
                anyhow::bail!("Failed to restore execution state");
            }
        }

        Ok(())
    }
}

impl Drop for SleepInhibitor {
    fn drop(&mut self) {
        cfg_if::cfg_if! {
            if #[cfg(unix)] {
                if let Some(mut child) = self.child.take() {
                    let _ = child.kill();
                }
            } else if #[cfg(windows)] {
                self.is_active.store(false, Ordering::SeqCst);

                unsafe {
                    let _ = SetThreadExecutionState(EXECUTION_STATE::ES_CONTINUOUS);
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_sleep_inhibitor_creation() {
        // This test may fail on systems without sleep inhibition support
        // but should not panic
        let _inhibitor = SleepInhibitor::new().await;
    }
}

